Skip to content

fix(clade): name every code, and stop the TSV reader eating empty fields - #50

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/clade-name-validated
Jul 17, 2026
Merged

fix(clade): name every code, and stop the TSV reader eating empty fields#50
hyperpolymath merged 1 commit into
mainfrom
feat/clade-name-validated

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Two problems, both in how a CLADE.a2ml gets written. The second one defeats my own merged #48 — details below.

1. A code cannot express a wrong belief, so it cannot be checked for one

CLADE-003 checks the code is one of the 12. It cannot check the author meant that clade — every code is valid whatever you thought it stood for. So these passed every gate:

repo wrote believing pt/gv actually mean
paint-type primary = "pt" "PainT-type" Protocols & Interop (it is an image editor → ap)
gossamer primary = "gv" "Graphical/Visual" Governance & Standards (there is no graphical clade → dx)

The two letters abbreviate the CLADE's name. They never abbreviate the REPO's name. It is a closed taxonomy of categories, not an abbreviation scheme — but nothing said so at the point of authoring, and nothing could catch it.

Fix: write the name beside the code, and check the pair

[clade]
primary      = "ap"
primary-name = "Applications"

CLADE-006 (BLOCK, pre-commit) rejects any pair disagreeing with verisim/seed/clades.a2ml. The redundancy is the point — the pair can express the wrong belief, so the pair can be rejected. Verified on all 10 cases, including both real historical errors:

ap|Applications              PASS
dx|Developer Ecosystem       PASS
gv|Governance & Standards    PASS   (ampersand survives)
pt|PainT-type                FAIL   <- the actual paint-type error
gv|Graphical/Visual          FAIL   <- the actual gossamer error
pt|Applications              FAIL   valid code, wrong name
ap|Protocols & Interop       FAIL   valid name, wrong code
zz|Applications              FAIL   invalid code
ap|<missing>                 FAIL   cannot omit the name
UNASSIGNED|UNASSIGNED        FAIL   template default fails loudly

clades.a2ml and the spec now state the rule and name both real errors, so the next author reads why before choosing. deploy-clade-a2ml.sh emits primary-name and refuses to write an identity it cannot name.

2. The TSV reader collapsed empty fields — 153 files already corrupted

Tab is IFS whitespace, so IFS=$'\t' read treats a run of tabs as one delimiter: empty fields vanish and everything after shifts left. 316 of 319 seed entries have an empty parent — this was the normal case, not an edge case:

proven <tab> fv <tab> [] <tab> standalone <tab><tab> Formally verified… <tab> hyperpolymath
                                                  ^^ empty parent collapses
-> parent="Formally verified…"   description="hyperpolymath"   owner=""

The damage is real and committed — 153 CLADE.a2ml files carry rationale = "" with the repo's description sitting in parent:

panic-attack   parent = "Security scanning tool — static analysis…"
flat-mate      parent = "Flat/apartment sharing management…"
rpa-elysium    parent = "Robotic Process Automation framework…"
live-files     parent = "Live file synchronisation tool…"

This defeated #48, and #48's own test could not see it

The owner support I added in #48 never worked here: $7 was always empty, so owner always fell back to hyperpolymath and an explicit owner = "metadatastician" was ignored. #48's byte-identical regression test passed because export-json.sh uses awk -F'\t', which does not collapse — the two derivation sites disagreed and only the tested one was correct. My apologies: I shipped that.

Fix

Translate tabs to US (0x1f) before reading. It is not IFS whitespace so runs are not collapsed, it cannot occur in the data (0 occurrences in repos.a2ml), and it is exactly what the ASCII unit separator is for. The parser's TSV contract is unchanged.

Verification

Scratch tree, end to end:

proven    (empty parent — the 316-entry case)
  rationale = "Formally verified safety library — Idris2 dependent types…"   (was "")
  parent    = ""                                                             (was the description)
  uuid      = ffa8e1f5-…                                                     (unchanged, matches registry)

cadastra  (owner = "metadatastician" in seed)
  uuid          = da97ea76-1715-5331-963b-27f011b10d22    <- correct; #48 works for the first time
  primary-owner = "metadatastician"

Also: repos.json regenerates byte-identical (no uuid moves), both a2ml files still parse as TOML (the dogfood gate uses tomllib), and the spec renders under asciidoctor.

Not in this PR

Repairing the 153 already-corrupted files is a separate, owner-facing job — this only stops the script producing more. Related: #49.

Draft: this changes the clade contract for every repo, so it is yours to review.

🤖 Generated with Claude Code

Two problems, both in how a CLADE.a2ml gets written.

── 1. A code cannot express a wrong belief, so it cannot be checked for one ──

CLADE-003 checks the primary code is one of the 12. It cannot check the author
meant that clade. Every code is valid whatever you thought it stood for, so these
passed every gate:

    paint-type   primary = "pt"   believing "PainT-type"
                 pt is Protocols & Interop. paint-type is an image editor (ap).
    gossamer     primary = "gv"   believing "Graphical/Visual"
                 gv is GoVernance & Standards. There is no graphical clade (dx).

The two letters abbreviate the CLADE's name. They never abbreviate the REPO's
name. This is a closed taxonomy of categories, not an abbreviation scheme — but
nothing said so at the point of authoring, and nothing could catch it.

Fix: every CLADE.a2ml now declares the name beside the code —

    [clade]
    primary      = "ap"
    primary-name = "Applications"

— and CLADE-006 (BLOCK, pre-commit) rejects a pair that disagrees with
verisim/seed/clades.a2ml. The redundancy is the point: the pair CAN express the
wrong belief, so the pair can be rejected. Verified against all 10 cases,
including the two real historical errors:

    ap|Applications                 PASS
    dx|Developer Ecosystem          PASS
    gv|Governance & Standards       PASS   (ampersand survives)
    pt|PainT-type                   FAIL   <- the actual paint-type error
    gv|Graphical/Visual             FAIL   <- the actual gossamer error
    pt|Applications                 FAIL   valid code, wrong name
    ap|Protocols & Interop          FAIL   valid name, wrong code
    zz|Applications                 FAIL   invalid code
    ap|<missing>                    FAIL   cannot omit the name
    UNASSIGNED|UNASSIGNED           FAIL   template default fails loudly

clades.a2ml and the spec now state the rule and name both real errors, so the
next author reads why before choosing. deploy-clade-a2ml.sh emits primary-name,
and refuses to write an identity it cannot name.

── 2. The TSV reader collapsed empty fields — 153 files already corrupted ──

TAB IS IFS WHITESPACE, so `IFS=$'\t' read` treats a run of tabs as ONE delimiter:
empty fields vanish and everything after them shifts left. 316 of the 319 seed
entries have an empty `parent`, so this was the normal case, not an edge case:

    proven <tab> fv <tab> [] <tab> standalone <tab><tab> Formally verified… <tab> hyperpolymath
                                                      ^^ empty parent collapses
    -> parent="Formally verified…"  description="hyperpolymath"  owner=""

The damage is real and committed — 153 CLADE.a2ml files across the estate carry
`rationale = ""` with the repo's DESCRIPTION sitting in `parent`:

    panic-attack   parent = "Security scanning tool — static analysis…"
    flat-mate      parent = "Flat/apartment sharing management…"
    rpa-elysium    parent = "Robotic Process Automation framework…"
    live-files     parent = "Live file synchronisation tool…"

This also silently defeated the `owner` support added in #48: $7 was always empty,
so owner always fell back to "hyperpolymath" and an explicit
`owner = "metadatastician"` in the seed was ignored. #48's own regression test
did not catch it because export-json.sh uses `awk -F'\t'`, which does NOT
collapse — the two derivation sites disagreed, and only the tested one was right.

Fix: translate the tabs to US (0x1f) before reading. 0x1f is not IFS whitespace,
so runs are not collapsed; it cannot occur in the data (0 occurrences in
repos.a2ml); it is exactly what the ASCII unit separator is for. The parser's TSV
contract is unchanged.

Verified end-to-end on a scratch tree:

    proven    (empty parent, the 316-entry case)
      rationale = "Formally verified safety library — Idris2 dependent types…"  (was "")
      parent    = ""                                                            (was the description)
      uuid      = ffa8e1f5-…                                                    (unchanged, matches registry)
    cadastra  (owner = "metadatastician" in seed)
      uuid          = da97ea76-1715-5331-963b-27f011b10d22   (correct — #48 works for the first time)
      primary-owner = "metadatastician"

Also verified: repos.json regenerates BYTE-IDENTICAL (no uuid moves), both a2ml
files still parse as TOML (the dogfood gate uses tomllib), and the spec renders.

Repairing the 153 already-corrupted files is a separate, owner-facing job — this
only stops the script producing more.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 17, 2026 04:11
@hyperpolymath
hyperpolymath merged commit 3fe5793 into main Jul 17, 2026
23 checks passed
@hyperpolymath
hyperpolymath deleted the feat/clade-name-validated branch July 17, 2026 04:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant